#include "Array.hpp"

Array::Array():
    width_(2),h_(1.0/(width_-1))
{
    array_dyn = new real [width_*width_] ();
}

//copy constructor
Array::Array(const Array& s):
    width_(s.width_),h_(s.h_)
{
    delete [] array_dyn;
    array_dyn = new real [width_*width_];
    for(size_t i=0; i<width_*width_; ++i)
        array_dyn[i]=s.array_dyn[i];
}

//assignment operator
Array& Array::operator= (const Array& s)
{
    delete [] array_dyn;
    width_= s.width_;
    h_ = s.h_;
    array_dyn = new real [width_*width_];
    for(size_t i=0; i<width_*width_; ++i)
        array_dyn[i]=s.array_dyn[i];
    return *this;
}

//print function for command line
void Array::print(std::ostream& out)
{
    out << YELLOW <<"\n" ;
    for ( size_t l=0; l<width_; ++l)
        out << "======  " ;
    out << std::endl;
    for ( size_t i=(width_-1); i>0; --i)
    {
        for ( size_t j=0; j<width_; ++j)
            out << std::fixed <<std::setprecision(5) <<array_dyn[(i*width_+j)] << " ";
        out << "\n";
    }
    for ( size_t j=0; j<width_; ++j)
        out << std::fixed <<std::setprecision(5) <<array_dyn[j] << " ";
    out << WHITE <<"\n" ;
}

//print function for gnuplot compatible file
void Array::printGNUPLOT(std::ostream& out)
{
    out <<"# X\tY\tvalue\n";
    int sizeArray=width_*width_;
    int auxX=0;
    for(int i=0; i<sizeArray; ++i)
    {
        out <<auxX*h_<<"\t"<<(i-auxX*width_)*h_<<"\t"<<array_dyn[i]<<"\n";
        if((i+1)%width_==0)
        {
            ++auxX;
            out<<"\n";
        }
    }
}

//resizing function
void Array::resize(size_t level)
{
    delete [] array_dyn;
    width_= pow(2,level)+1;
    h_ = 1.0/(width_-1.0);
    array_dyn = new real [width_*width_] ();
}

//computing L2-Norm
real Array::l2Norm() const
{
    real norm=0.0;
    for ( size_t q = 1; q <width_-1; ++q)
    {
        for ( size_t p = 1; p <width_-1; ++p)
            norm +=(array_dyn[q*width_+p])*(array_dyn[q*width_+p])  ;
    }
    norm= sqrt(norm/((width_-2)*(width_-2)));
    return norm;
}

//fill Array with given value
void Array::fill(const real& in)
{
    for ( size_t n=0; n<width_*width_ ; ++n)
        array_dyn[n]=in;
}

//set Dirichlet condtion at specified boundary side
void Array::setDirichletBoundary(std::function<real(real,real)> g, BOUNDARY bound)
{
    if(bound==NORTH)
    {
        for (size_t p = 0; p <width_; ++p)
            array_dyn[(width_-1)*width_+p] = g((width_-1)*h_,p*h_);
    }
    else if(bound==SOUTH)
    {
        for (size_t p=0; p<width_; ++p)
            array_dyn[p] = g(0,p*h_);
    }
    else if(bound==WEST)
    {
        for (size_t q = 0; q<width_; ++q)
            array_dyn[q*width_] = g(q*h_,0.0);
    }
    else
    {
        for (size_t q=0; q<width_; ++q)
            array_dyn[q*width_+(width_-1)] = g(q*h_,(width_-1)*h_);
    }
}

//set all boundary values with the same Dirichlet condition
void Array::setAllDirichletBoundary(std::function<real(real,real)> g)
{
    for ( size_t q = 0; q <width_; ++q)
    {
        //west boundary
        array_dyn[q*width_] = g(q*h_,0.0);
        //east boundary
        array_dyn[q*width_+(width_-1)] = g(q*h_,(width_-1)*h_);
    }
    for ( size_t p = 0; p <width_; ++p)
    {
        //south boundary
        array_dyn[p] = g(0,p*h_);
        //north boundary
        array_dyn[(width_-1)*width_+p] = g((width_-1)*h_,p*h_);
    }
}
